package com.ideaaedi.zoo.diy.artifact.mybatisplus;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.ideaaedi.commonds.time.DateTimeConverter;
import com.ideaaedi.zoo.commonbase.component.auth.AuthUrlWhitelist;
import com.ideaaedi.zoo.commonbase.constant.AutoConfigurationConstant;
import com.ideaaedi.zoo.diy.artifact.mybatisplus.handler.FullSqlPreparePreHandler;
import com.ideaaedi.zoo.diy.artifact.mybatisplus.handler.MybatisPlusMetaObjectHandler;
import com.ideaaedi.zoo.diy.artifact.mybatisplus.injector.MybatisPlusSqlInjector;
import com.ideaaedi.zoo.diy.artifact.mybatisplus.interceptor.SqlPreparePreInterceptor;
import com.ideaaedi.zoo.diy.artifact.mybatisplus.properties.ZooMybatisPlusDIYGuideProperties;
import com.ideaaedi.zoo.diy.artifact.mybatisplus.provider.DefaultOperatorIdProvider;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;

/**
 * MybatisPlus ext 自动配置
 */
@Slf4j
@Import(MybatisPlusSqlInjector.class)
@EnableConfigurationProperties(ZooMybatisPlusDIYGuideProperties.class)
@AutoConfigureOrder(AutoConfigurationConstant.ZOO_MYBATIS_PLUS_AUTO_CONFIGURATION_ORDER)
@ConditionalOnProperty(value = ZooMybatisPlusAutoConfiguration.ENABLE, havingValue = "true", matchIfMissing = true)
public class ZooMybatisPlusAutoConfiguration {
    
    public static final String ENABLE = "zoo.switch.artifact-mybatis-plus.enable";

    @Bean
    @ConditionalOnProperty(value = "zoo.artifact-mybatis-plus.jackson-type-handler-initializer.enable", havingValue = "true", matchIfMissing = true)
    public InitializingBean jacksonTypeHandlerInitializer() {
        return () -> {
            ObjectMapper om = JacksonTypeHandler.getObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

            // 注册JavaTimeModule以支持Java 8时间类（LocalDateTime、LocalDate、LocalTime等）
            JavaTimeModule javaTimeModule = new JavaTimeModule();
            DateTimeFormatter formatter = DateTimeConverter.REGULAR_DATE_TIME.getFormatter();
            javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
            om.registerModule(javaTimeModule);

            // 自动扫描注册项目中存在的其它模块
            om.findAndRegisterModules();
        };
    }

    @Bean(name = AutoConfigurationConstant.MYBATIS_PLUS_INTERCEPTOR_BEAN_NAME)
    @ConditionalOnMissingBean(name = {
            AutoConfigurationConstant.ZOO_TENANT_AUTO_CONFIGURATION_BEAN_NAME,
            AutoConfigurationConstant.MYBATIS_PLUS_INTERCEPTOR_BEAN_NAME
    })
    public MybatisPlusInterceptor mybatisPlusInterceptor(List<FullSqlPreparePreHandler> fullSqlPreparePreHandlers) {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        if (!CollectionUtils.isEmpty(fullSqlPreparePreHandlers)) {
            interceptor.addInnerInterceptor(new SqlPreparePreInterceptor(fullSqlPreparePreHandlers));
        }
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        log.info("enable mybatis-plus interceptors -> {}.",
                interceptor.getInterceptors().stream().map(x -> x.getClass().getSimpleName()).collect(Collectors.toList()));
        return interceptor;
    }
    
    @Bean
    @ConditionalOnMissingBean(MetaObjectHandler.class)
    public MetaObjectHandler defaultMetaObjectHandler(
            @Autowired(required = false)  MybatisPlusMetaObjectHandler.OperatorIdProvider operatorIdProvider) {
        return new MybatisPlusMetaObjectHandler(operatorIdProvider);
    }
    
    @Bean
    @ConditionalOnMissingBean(MetaObjectHandler.class)
    public MybatisPlusMetaObjectHandler.OperatorIdProvider defaultOperatorIdProvider(
            @Autowired(required = false) AuthUrlWhitelist authUrlWhitelist) {
        return new DefaultOperatorIdProvider(authUrlWhitelist);
    }
}
